package test.demos.redislock.pojo;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import test.demos.utils.DuRedisUtils;

/**
 * 基于Redis支持分布式线程锁 
 *Author: wuwei 
 * Version: 1.0 Create Date Time:
 * 2018年1月26日 下午4:29:36. Update Date Time:
 * @see
 */
@Component
public class RedisDistributionLock implements DistributionLock {

    private static final long LOCK_TIMEOUT = 20 * 1000; // 加锁超时时间 单位毫秒

    private static final Logger logger = LoggerFactory.getLogger(RedisDistributionLock.class); // redis锁日志

    // @Resource(name="redisTemplate")
    // private RedisTemplate<Serializable, Serializable> redisTemplate;
    @Autowired
    DuRedisUtils duRedisUtils;

    /**
     * 取到锁加锁 取不到锁一直等待直到获得锁
     */
    @Override
    public Long lock(String lockKey) {
        while (true) { // 循环获取锁
            Long currentTimeFromReids = duRedisUtils.getCurrentTimeFromRedis();
            Long newLockTimeout = currentTimeFromReids + LOCK_TIMEOUT + 1; // 锁时间
            // 如果能设置成功，无其它线程占用锁
            if (duRedisUtils.setnx(lockKey, newLockTimeout)) {
                // 设置redis记录超时时间
                duRedisUtils.expire(lockKey, LOCK_TIMEOUT);
                return newLockTimeout;
            }
            else {
                Long currtLockTimeout = (Long) duRedisUtils.get(lockKey);
                // 判断锁是否超时，失效
                if (currtLockTimeout != null && currtLockTimeout < currentTimeFromReids) {
                    // 判断是否为空，不为空的情况下，说明已经失效，如果被其他线程设置了值，则第二个条件判断是无法执行
                    Long oldLockTimeout = (Long) duRedisUtils.getSet(lockKey, newLockTimeout);
                    // 如过这个时候，多个线程恰好都到了这里，但是只有一个线程的设置值和当前值相同，他才有权利获取锁
                    if (oldLockTimeout != null && oldLockTimeout.equals(currtLockTimeout)) {
                        logger.info("锁超时,应用内解锁！");
                        // 设置超时时间
                        duRedisUtils.expire(lockKey, LOCK_TIMEOUT);
                        return newLockTimeout;// 返回加锁时间
                    }
                }
            }
            try {
                TimeUnit.MILLISECONDS.sleep(100);// 睡眠100毫秒
            }
            catch (InterruptedException e) { }
        }
    }

    @Override
    public void unlock(String lockKey, long lockvalue) {
        Long currtLockTimeout = (Long) duRedisUtils.get(lockKey);
        // 加锁时间与解锁时间匹配才能解锁
        if (currtLockTimeout != null && currtLockTimeout == lockvalue) {
            // 删除键
            duRedisUtils.delete(lockKey);
        }
    }

    @Override
    public Long tryLock(String lockKey) {
        Long currentTimeFromReids = duRedisUtils.getCurrentTimeFromRedis();
        Long newLockTimeout = currentTimeFromReids + LOCK_TIMEOUT + 1; // 锁时间
        // 如果能设置成功，无其它线程占用锁
        if (duRedisUtils.setnx(lockKey, newLockTimeout)) {
            // 设置redis记录超时时间
            duRedisUtils.expire(lockKey, LOCK_TIMEOUT);
            return newLockTimeout;
        }
        return null;
    }

}
